package com.hhkj.admin.order.service.biz.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.hhkj.admin.common.cont.NumTypeEnum;
import com.hhkj.admin.common.event.EventPublisher;
import com.ruoyi.common.utils.num.NumUtils;
import com.hhkj.admin.order.common.component.checker.OrderChecker;
import com.hhkj.admin.order.common.component.checker.OrderItemChecker;
import com.hhkj.admin.order.common.cont.OrderConst;
import com.hhkj.admin.order.domain.Order;
import com.hhkj.admin.order.domain.OrderItem;
import com.hhkj.admin.order.event.OrderPaySuccessEvent;
import com.hhkj.admin.order.model.dopo.OrderDo;
import com.hhkj.admin.order.model.dopo.OrderItemDo;
import com.hhkj.admin.order.model.dopo.OrderItemPo;
import com.hhkj.admin.order.model.dopo.OrderPo;
import com.hhkj.admin.order.model.extend.SaleAndRootInfo;
import com.hhkj.admin.order.model.req.OrderReq;
import com.hhkj.admin.order.model.res.OrderRes;
import com.hhkj.admin.order.service.base.OrderItemService;
import com.hhkj.admin.order.service.base.OrderService;
import com.hhkj.admin.order.service.biz.OrderBizService;
import com.hhkj.admin.order.service.extend.OrderExtendService;
import com.hhkj.admin.sale.domain.Sale;
import com.hhkj.admin.sale.model.dopo.SaleDo;
import com.hhkj.admin.sale.model.dopo.SalePo;
import com.hhkj.admin.sale.service.base.SaleService;
import com.hhkj.admin.student.common.cont.StudentConst;
import com.hhkj.admin.student.common.utils.StudentUtils;
import com.hhkj.admin.student.domain.Student;
import com.hhkj.admin.student.domain.StudentBatch;
import com.hhkj.admin.student.domain.StudentCourse;
import com.hhkj.admin.student.model.dopo.StudentDo;
import com.hhkj.admin.student.service.base.StudentBatchService;
import com.hhkj.admin.student.service.base.StudentCourseService;
import com.hhkj.admin.student.service.base.StudentService;
import com.ruoyi.common.base.biz.service.biz.BaseBizServiceImpl;
import com.ruoyi.common.base.model.extend.ValidError;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.exception.GlobalErrorListException;
import com.ruoyi.common.exception.GlobalException;
import com.ruoyi.common.exception.excel.ExcelImportException;
import com.ruoyi.common.model.excel.res.ExcelImportErrorData;
import com.ruoyi.common.model.excel.res.ExcelImportErrorRes;
import com.ruoyi.common.model.excel.res.ExcelImportRes;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.excl.model.ExcelCreateModel;
import com.ruoyi.common.utils.excl.utils.ExcelUtils;
import com.ruoyi.system.service.ISysUserService;
import org.apache.poi.ss.usermodel.Workbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class OrderBizServiceImpl
        extends BaseBizServiceImpl<Order, OrderReq, OrderRes, OrderDo, OrderPo, OrderService, OrderExtendService>
        implements OrderBizService {

    @Autowired
    private SaleService saleService;
    @Autowired
    private OrderItemService orderItemService;
    @Autowired
    private ISysUserService iSysUserService;
    @Autowired
    private StudentBatchService studentBatchService;
    @Autowired
    private StudentService studentService;
    @Autowired
    private StudentCourseService studentCourseService;
    @Autowired
    private EventPublisher eventPublisher;
    @Autowired
    private OrderItemChecker orderItemChecker;
    @Autowired
    private OrderChecker orderChecker;
    @Autowired
    private OrderBizService orderBizService;

    private final Object validateAndPayOrderSync = new Object();

    private static final Logger logger = LoggerFactory.getLogger(OrderBizService.class);

    @Override
    public Workbook createExportWorkbook(OrderReq orderReq) {
        List<OrderRes> list = this.list(orderReq);
        String suffix = "";
        if (ObjectUtil.isNotEmpty(orderReq.getCreateDateStart()) && ObjectUtil.isNotEmpty(orderReq.getCreateDateEnd())) {
            suffix = " 创建日期：" + DateUtil.format(orderReq.getCreateDateStart(), "yyyy-MM-dd") + " - " + DateUtil.format(orderReq.getCreateDateEnd(), "yyyy-MM-dd");
        }
        Workbook template = ExcelUtils.createExcelWorkbookSheetNumber(new ExcelCreateModel(
                OrderConst.EXPORT_EXCEL_TITLE,
                OrderConst.EXPORT_EXCEL_TITLE + suffix,
                list,
                OrderRes.class));
        return template;
    }

    @Override
    public Workbook createImportTemplate() {
        Workbook template = ExcelUtils.createExcelWorkbookSheet(new ExcelCreateModel(
                OrderConst.ORDER_IMPORT_TEMPLATE_NAME,
                OrderConst.ORDER_IMPORT_TEMPLATE_NAME,
                new ArrayList<>(OrderConst.ORDER_IMPORT_TEMPLATE_LIST),
                OrderItem.class));
        return template;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Order validateAndCreateOrderByOrderItemList(Long saleId,
                                                         List<OrderItem> orderItemList,
                                                         OrderConst.SourceTypeCode sourceTypeCode,
                                                         Long createBy) {
        List<ValidError<OrderItem>> validErrorList = orderItemChecker.checkBeforeCreateOrder(orderItemList, sourceTypeCode);
        if (CollectionUtil.isEmpty(validErrorList) == false) {
            if (sourceTypeCode.equals(OrderConst.SourceTypeCode.APP)) {
                String message = orderItemChecker.assembleMessage(validErrorList);
                throw new GlobalErrorListException(validErrorList, message);
            }
            List<ExcelImportErrorData> excelImportErrorDataList = new ArrayList<>();
            validErrorList.stream().forEach(validError -> {
                ExcelImportErrorData<OrderItem> orderItemExcelImportErrorData = new ExcelImportErrorData<>();
                orderItemExcelImportErrorData.setOrderNum(validError.getOrderNum() + 2);
                orderItemExcelImportErrorData.setData(validError.getData());
                orderItemExcelImportErrorData.setMessage(CollectionUtil.getFirst(validError.getErrorInfoList()));
                excelImportErrorDataList.add(orderItemExcelImportErrorData);
            });
            throw new ExcelImportException(orderItemList.size(), excelImportErrorDataList);
//            throw new GlobalErrorListException(validErrorList);
        }
        return saveOrderAndItem(saleId, orderItemList, sourceTypeCode, createBy);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ExcelImportRes validateAndCreateOrderByExcel(Long saleId, MultipartFile file, OrderConst.SourceTypeCode sourceTypeCode, Long createBy) {
        //获取导入的数据
        List<OrderItem> orderItemList = listByExcel(file);
        validateAndCreateOrderByOrderItemList(saleId, orderItemList, sourceTypeCode, createBy);
        return new ExcelImportRes(orderItemList.size());
    }

    /**
     * 读取excel数据，以list格式返回
     * @param file
     * @return
     */
    private List<OrderItem> listByExcel(MultipartFile file) {
        try {
            return ExcelUtils.importExcel(file.getInputStream(), OrderItem.class, null);
        } catch (IOException e) {
            throw new GlobalException("导入失败，请检查格式");
        }
    }

    /**
     * 创建并保存订单同时保存订单item（不进行校验，调用时需要保证订单内容没有问题）
     *
     * @param saleId        订单所属销售
     * @param orderItemList 订单item列表
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Order saveOrderAndItem(Long saleId, List<OrderItem> orderItemList, OrderConst.SourceTypeCode sourceTypeCode, Long createBy) {
        Order order = new Order();
        SaleAndRootInfo saleAndRoot = getSaleAndRoot(saleId);
        //获取商家信息
        Sale sale = saleAndRoot.getSale();
        //设置商家id
        order.setSaleId(saleId);
        //设置商家名字
        order.setSaleName(sale.getName());
        //设置商家编号
        order.setSaleNum(sale.getNum());

        //获取销售信息
        Sale root = saleAndRoot.getRoot();
        //设置销售id
        order.setSaleRootId(root.getId());
        //设置销售名字
        order.setSaleRootName(root.getName());
        //设置销售编号
        order.setSaleRootNum(root.getNum());

        //设置订单编号
        long count = this.getService().countByToday();
        order.setNum(NumUtils.generateNum(NumTypeEnum.ORDER.getPrefix(), ++count));
        //设置订单来源
        order.setSourceTypeCode(sourceTypeCode.getCode());
        //设置学生数量
        order.setItemAmount(orderItemList.size());
        //应付金额
        BigDecimal price = orderItemList.stream().map(OrderItem::getPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
        order.setPrice(price);
        //设置订单状态
        order.setStatusCode(OrderConst.StatusEnum.PAYING.getCode());
        //设置订单创建者
        order.setCreateBy(createBy);

        //保存订单
        if (this.getService().save(order) == false) {
            throw new GlobalException("订单创建失败！");
        }

        //保存订单item
        orderItemList.forEach(orderItem -> {
            orderItem.setOrderId(order.getId());
            orderItem.setCreateBy(createBy);
        });
        orderItemService.saveBatch(orderItemList);
        return order;
    }

    @Override
    public Order validateAndPayOrder(OrderReq orderReq, Long createBy) {
        if (ObjectUtil.isEmpty(orderReq)) {
            throw new GlobalException("订单状态异常，请联系管理员");
        }
        Order order = null;
        synchronized (validateAndPayOrderSync) {
            order = orderBizService.validateBeforePayOrder(orderReq.getId(), createBy);
            order = orderBizService.afterPaySuccess(orderReq, createBy);
        }
        return order;
    }

    @Override
    public Order validateBeforePayOrder(Long orderId, Long createBy) {
        //校验订单
        Order order = orderChecker.checkBeforePayOrder(orderId);
        order.setUpdateBy(createBy);
        //校验子订单
        orderItemChecker.checkBeforePayOrder(orderId);
        return order;
    }

    @Override
    public Order afterPaySuccess(OrderReq orderReq, Long createBy) {
        Long orderId = orderReq.getId();
        Order order = getExtendService().getByIdNotNull(orderId);
        try {
            OrderItemDo orderItemDo = new OrderItemDo();
            orderItemDo.setOrderId(orderId);
            List<OrderItemPo> orderItemPoList = orderItemService.listPoByDo(orderItemDo);
            orderBizService.afterPaySuccess(order, orderItemPoList, orderReq, createBy);
        } catch (Exception e) {
            //订单都不存在
            if (ObjectUtil.isEmpty(orderReq)) {
                logger.error("订单支付异常，订单不存在,订单id：{}, 报错信息：{}", orderReq.getId(), e.getMessage());
                throw new GlobalException(e.getMessage());
            }
            logger.error("订单支付异常,订单id：{}, 订单编号：{}, 报错信息：{}", order.getId(), order.getNum(), e.getMessage());
            //修改订单为异常状态订单
            order.setStatusCode(OrderConst.StatusEnum.ERROR.getCode());
            this.getService().updateById(order);
            //向上抛
            throw e;
        }
        return order;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Order afterPaySuccess(Order order, List<? extends OrderItem> orderItemList, OrderReq orderReq, Long createBy) {
        //设置与支付相关的字段
        setOrderPayField(order, orderReq);

        //保存批次
        SysUser defaultIns = iSysUserService.getPoByDo(new SysUser().setDefaultFlag(1));
        StudentBatch studentBatch = saveStudentBatch(defaultIns.getUserId(), order, createBy);

        //保存学员
        List<Student> studentList = saveStudent(orderItemList);

        //保存学员课程关系
        List<StudentCourse> studentCourseList = saveStudentCourse(studentList, orderItemList, order, studentBatch, createBy);

        //更新子订单状态，绑定子订单学员与报名课程的关系
        if (orderItemList.size() != studentCourseList.size()) {
            throw new GlobalException("订单出现异常，请联系管理员");
        }
        for (int i = 0; i < orderItemList.size(); i++) {
            orderItemList.get(i).setStudentCourseId(studentCourseList.get(i).getId());
            orderItemList.get(i).setUpdateBy(createBy);
        }
        orderItemService.updateBatchById(BeanUtil.copyToList(orderItemList, OrderItem.class));

        //更新订单状态
        order.setUpdateBy(createBy);
        this.getService().updateById(order);

        eventPublisher.sendEvent(new OrderPaySuccessEvent(order));
        return order;
    }

    @Override
    public SaleAndRootInfo getSaleAndRoot(Long saleId) {
        SaleAndRootInfo saleAndRootInfo = new SaleAndRootInfo();
        //获取商家信息
        SaleDo saleDo = new SaleDo();
        saleDo.setId(saleId);
        SalePo sale = saleService.getPoByDo(saleDo);
        if (ObjectUtil.isEmpty(sale)) {
            throw new GlobalException("所属商家不存在，请填写正确的商家");
        }
        saleAndRootInfo.setSale(sale);

        //获取销售信息
        saleDo.setId(sale.getRootId());
        SalePo root = saleService.getPoByDo(saleDo);
        if (ObjectUtil.isEmpty(root)) {
            throw new GlobalException("所属销售不存在，请联系管理员");
        }
        saleAndRootInfo.setRoot(root);
        return saleAndRootInfo;
    }

    /**
     * 设置与订单对象内与支付相关的订单属性
     *
     * @param order    订单
     * @param orderReq 支付请求，orderReq 至少要包括id，支付方式payMethodCode，实付金额payPrice, 打款时间payTime
     * @return
     */
    private void setOrderPayField(Order order, OrderReq orderReq) {
        //填充订单信息
        if (order.getPrice().compareTo(orderReq.getPayPrice()) != 0) {
            throw new GlobalException("实付金额与应付金额不符，请检查");
        }
        if (orderReq.getPayPrice().equals(0)) {
            throw new GlobalException("订单支付金额为0，未支付成功");
        }
        order.setPayPrice(orderReq.getPayPrice());
        order.setPayMethodCode(orderReq.getPayMethodCode());
        order.setPayTime(orderReq.getPayTime());
        order.setPayReceiptUrl(orderReq.getPayReceiptUrl());
        order.setPayDescription(orderReq.getPayDescription());
        order.setTransactionId(orderReq.getTransactionId());
        order.setFinalPrice(orderReq.getPayPrice());
        order.setPayAccount(orderReq.getPayAccount());
        order.setStatusCode(OrderConst.StatusEnum.PAID.getCode());
    }

    /**
     * 保存学员批次
     *
     * @param insId    学员机构id
     * @param order    订单
     * @param createBy 创建者
     * @return
     */
    private StudentBatch saveStudentBatch(Long insId, Order order, Long createBy) {

        StudentBatch studentBatch = StudentBatch.builder()
                .insId(insId)
                .payFlag(1l)
                .payDate(order.getPayTime())
                .payPrice(order.getPayPrice())
                .payProofUrl(order.getPayReceiptUrl())
                .status(StudentConst.AuditCode.STATUS_PASS.getCode())
                .build();
        studentBatch.setCreateBy(createBy);
        studentBatchService.save(studentBatch);
        return studentBatch;
    }

    /**
     * 将子订单的学员，转化为系统中的学员账号，初始化学员账号，可以登录学习
     * @param orderItemList 子订单列表
     * @return
     */
    private List<Student> saveStudent(List<? extends OrderItem> orderItemList) {
        StudentDo studentDo = new StudentDo();
        studentDo.setIdCardList(orderItemList.stream().map(OrderItem::getIdCard).collect(Collectors.toList()));
        List<Student> studentList = studentService.listStudentPoByDo(studentDo);
        Set<String> idCardSet = studentList.stream().map(Student::getIdCard).collect(Collectors.toSet());
        List<Student> studentListToSave = orderItemList.stream().filter(orderItemPo -> {
            String idCard = orderItemPo.getIdCard();
            if (idCardSet.contains(idCard)) {
                return false;
            }
            idCardSet.add(idCard);
            return true;
        }).map(orderItemPo -> {
            Student student = new Student();
            student.setName(orderItemPo.getName());
            student.setPhone(orderItemPo.getPhone());
            student.setIdCard(orderItemPo.getIdCard());
            student.setCreateBy(iSysUserService.getDefaultUser().getUserId());
            return student;
        }).collect(Collectors.toList());
        StudentUtils.batchEncryptPassword(studentListToSave);
        studentService.saveBatch(studentListToSave);
        studentList.addAll(studentListToSave);
        return studentList;
    }

    /**
     * 将学员和学员报名的课程建立关联，保存至关联表内
     * @param studentList 学员列表
     * @param orderItemList 子订单列表
     * @param order 订单信息
     * @param studentBatch 学员批次
     * @param createBy 创建人
     * @return List<StudentCourse> 返回保存的学员课程信息
     */
    private List<StudentCourse> saveStudentCourse(List<Student> studentList,
                                   List<? extends OrderItem> orderItemList,
                                   Order order,
                                   StudentBatch studentBatch,
                                   Long createBy) {
        //获取学员（idCard-学员信息）map映射表,
        Map<String, Student> idcardStudentMap = studentList.stream().collect(Collectors.toMap(Student::getIdCard, student -> student));
        //设置学员课程属性
        List<StudentCourse> studentCourseList = orderItemList.stream().map(orderItem -> {
            DateTime dateTime = DateUtil.offsetDay(order.getPayTime(), orderItem.getDayAmount() + 1);
            String format = DateUtil.format(dateTime, "yyyy-MM-dd");
            DateTime lineDate = DateUtil.parse(format);
            StudentCourse studentCourse = StudentCourse.builder()
                    .studentId(idcardStudentMap.get(orderItem.getIdCard()).getId())
                    .courseId(orderItem.getCourseId())
                    .batchId(studentBatch.getId())
                    .courseBankTypeId(orderItem.getCourseBankTypeId())
                    .startDate(order.getPayTime())
                    .lineDate(lineDate)
                    .insId(studentBatch.getInsId())
                    .status("2")
                    .unitPayPrice(orderItem.getPrice())
                    .build();
            studentCourse.setCreateBy(createBy);
            return studentCourse;
        }).collect(Collectors.toList());
        studentCourseService.saveBatch(studentCourseList);
        return studentCourseList;
    }
}
